/**
 * @file    client.cpp
 * @author  Siddharth Sathyam <ssathy2@gmail.com>
 * @author  Alan Ruvalcaba <aruval3@gmail.com>
 * @author  Pat LaMontagna <plamontagna@gmail.com>
 * @version 1.0
 * @section LICENSE
 *
 * This program is property of Siddharth Sathyam, Alan Ruvalcaba, Pat LaMontagna.
 *
 * @section DESCRIPTION
 *
 * The client class is the container for the socket of the user and allows the UI to communicate
 * with the server.
 */

#include "client.h"

Client::Client(QString address, quint16 port,QString userName)
{
    client = new QTcpSocket(this);
    this->address = address;
    this->port = port;
    this->userName = userName;

    connect(client, SIGNAL(connected()),this, SLOT(sendUserInformation()));
    connect(client, SIGNAL(readyRead()),this, SLOT(readyRead()));
}

bool Client::startClient(){

    // Get user IP Address
    QHostAddress addr(address);

    // Connect the client to the server
    client->connectToHost(addr, port);

    // Check for client timeout
    if(client->waitForConnected(10000))
    {
	QMessageBox *success = new QMessageBox(0);
	const QString connected = "Connected to host: " + address + " as " + userName ;
	success->setText(connected);
	success->show();
	return true;
    }
    else
    {
	QMessageBox *failure = new QMessageBox(0);
	const QString notConnected = "Failed to connect to host: " + address + " Please Try Again";
	failure->setText(notConnected);
	failure->show();
	return false;
    }
}


void Client::sendUserInformation()
{
    QByteArray datablock;
    QDataStream sendStream(&datablock,QIODevice::WriteOnly);
    sendStream.setVersion(QDataStream::Qt_4_0);

    // [0] tells the server we're sending a username
    sendStream << quint16(0)<< this->getUserName();

    // Send the datablock username out
    client->write(datablock);
    client->flush();
    client->waitForBytesWritten();
}

void Client::readyRead()
{
    QDataStream recvStream(client);
    recvStream.setVersion(QDataStream::Qt_4_7);
    quint16 packId = 0;
    quint16 blockSize = 0;

    // read in data from the socket in accordance with the protocol that was decided
    if(blockSize == 0)
    {
	if(client->bytesAvailable() < sizeof(quint16))
	{
	    return;
	}
	recvStream >> packId;

	switch(packId)
	{
	    case(1):
	    {
                    // <1> <Receive a message>
		blockSize = sizeof(QString) + sizeof(QString) + sizeof(QString) + sizeof(QString);

		if (client->bytesAvailable() <  blockSize)
		{
		    return;
		}


                QDataStream recvStream(this->client);
                recvStream.setVersion(QDataStream::Qt_4_7);

                QString toUser;
                QString fromUser;
                QString timeSent;
                QString message;

                // Break up the message into its individual parts and save it to the variables
                recvStream >> fromUser >> toUser >> message >> timeSent;

                emit receiveMessage(fromUser,toUser,message,timeSent);
		break;
	    }

	    // <2> <QStringList of all friends online>
	    case(2):
	    {
		blockSize = sizeof(QStringList);

		if (client->bytesAvailable() <  blockSize)
		{
		    return;
		}

		// List of all friends online are sent
		QStringList updatedListOfUsersOnline;
		recvStream >> updatedListOfUsersOnline;
		onlineUsers = (QStringList) updatedListOfUsersOnline;
		emit signalUsers(updatedListOfUsersOnline);
		break;
	    }

	    // Message Header <3> <QString userName added> <bool whether or not username was added or not>
	    case(3):
	    {
		blockSize = sizeof(QString) + sizeof(bool);

		if (client->bytesAvailable() <  blockSize)
		{
		    return;
		}

		QString userNameAttemptingToAdd = "";
		bool wasNameAdded = false;
		recvStream >> userNameAttemptingToAdd >> wasNameAdded;
		emit usernameValidConfirmation(userNameAttemptingToAdd, wasNameAdded);
		break;
	    }

	    // <6> <QString username requesting to add as friend>
	    case(6):
	    {
		blockSize = sizeof(QString);

		if (client->bytesAvailable() <  blockSize)
		{
		    return;
		}

		QString requestingUser = "";
		recvStream >> requestingUser;
		emit friendRequest(requestingUser);
		break;
	    }

	    // <5> <QString friendUsername to be added as a friend> <<bool> whether friend exists in server>
	    case(5):
	    {
		blockSize = sizeof(QStringList) + sizeof(bool);

		if (client->bytesAvailable() <  blockSize)
		{
		    return;
		}

		QString friendToBeAdded = "";
		bool isConfirmed = false;
		recvStream >> friendToBeAdded >> isConfirmed;
		emit friendExistsConfirmation(friendToBeAdded, isConfirmed);
		break;
	    }

	    // <9> <QString friendUsername to be removed> <<bool> whether friend exists in server>
	    case(9):
	    {
		blockSize = sizeof(QStringList) + sizeof(bool);

		if (client->bytesAvailable() <  blockSize)
		{
		    return;
		}

		QString friendToBeRemoved = "";
		bool isConfirmed = false;
		recvStream >> friendToBeRemoved >> isConfirmed;
		emit friendRemoveExistsConfirmation(friendToBeRemoved, isConfirmed);
		break;
	    }
	    default:
	    {
		//@TODO Invalid Pack id... do something about it here..
		break;
	    }
	}
    }
}

//Set of getters
QString Client::getAddress()
{
    return address;
}

QString Client::getUserName()
{
    return userName;
}

quint16 Client::getPort()
{
    return port;
}

QString Client::getMessage()
{
    return message;
}

QStringList Client::getOnlineList()
{
    return onlineUsers;
}
QTcpSocket *Client::getClient()
{
    return client;
}


void Client::sendMessage(QString fromUsername, QString toUsername, QString message, QString timeSent)
{
    QByteArray datablock;
    QDataStream sendStream(&datablock , QIODevice::WriteOnly);
    sendStream.setVersion(QDataStream::Qt_4_7);

    // message header 1 is intrepeted by the server as a message with to and from userNames
    sendStream << quint16(1) << (QString) fromUsername << (QString) toUsername << (QString) message << (QString) timeSent;
    client->write(datablock);
    client->flush();
    client->waitForBytesWritten();
}

void Client::handleOutgoingFriendRequest(QString buddy)
{
    QByteArray datablock;
    QDataStream sendStream(&datablock , QIODevice::WriteOnly);
    sendStream.setVersion(QDataStream::Qt_4_7);

    // message header 4 is sending a friend request to the buddy
    sendStream << quint16(4) << (QString) buddy;
    client->write(datablock);
    client->flush();
    client->waitForBytesWritten();
}

void Client::handleFriendRequestConfirmation(QString personRequesting, bool isFriend)
{
    QByteArray datablock;
    QDataStream sendStream(&datablock , QIODevice::WriteOnly);
    sendStream.setVersion(QDataStream::Qt_4_7);

    // message header 7 is sending a friend request to the buddy
    sendStream << quint16(7) << (QString) userName << (QString) personRequesting << (bool) isFriend;
    client->write(datablock);
    client->flush();
    client->waitForBytesWritten();
}

void Client::handleFriendRemoveRequest(QString personToRemove)
{
    QByteArray datablock;
    QDataStream sendStream(&datablock , QIODevice::WriteOnly);
    sendStream.setVersion(QDataStream::Qt_4_7);

    // message header 8 is sending a friend remove request
    sendStream << quint16(8) << (QString) personToRemove;
    client->write(datablock);
    client->flush();
    client->waitForBytesWritten();
}
